package admin

import (
	"context"
	"fmt"
	"grpc.getaway.adminspro.padmins.com/internal/rpc/module/admin/adminLogic"
	"grpc.getaway.adminspro.padmins.com/internal/rpc/utils/database"
	"grpc.getaway.adminspro.padmins.com/pkg/config/businessError"
	"grpc.getaway.adminspro.padmins.com/pkg/libs/db"
	"grpc.getaway.adminspro.padmins.com/pkg/libs/utils"
	pbAdmin "grpc.getaway.adminspro.padmins.com/pkg/proto/admin"
	"math/rand"
	"regexp"
	"strconv"
)

func (rpc *admin) AdminEdit(ctx context.Context, pb *pbAdmin.AdminEditReq) (*pbAdmin.AdminEditResp, error) {

	roleIds := make([]interface{}, len(pb.RoleId))
	for i, roleId := range pb.RoleId {
		roleIds[i] = roleId
	}

	matched, _ := regexp.MatchString("^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$", pb.Admin.Email)
	if matched != true {
		return nil, businessError.ErrorInfoToRpcError(businessError.AdminEmailError)
	}
	if pb.Admin.Nickname == "" {
		return nil, businessError.ErrorInfoToRpcError(businessError.AdminNicknameError)
	}
	matched, _ = regexp.MatchString("^[a-zA-Z][a-zA-Z0-9]{5,19}$", pb.Admin.Username)
	if matched != true {
		return nil, businessError.ErrorInfoToRpcError(businessError.AdminUsernameError)
	}
	if len(roleIds) == 0 {
		return nil, businessError.ErrorInfoToRpcError(businessError.AdminRoleIdsEmpty)
	}
	if _, ok := adminLogic.List()["status"][strconv.FormatInt(int64(pb.Admin.Status), 10)]; !ok {
		return nil, businessError.ErrorInfoToRpcError(businessError.AdminStatusError)
	}

	adminIds := []interface{}{}
	if pb.OperaterId != 0 {
		if pb.OperaterId == pb.Admin.Id {
			return nil, businessError.ErrorInfoToRpcError(businessError.AdminOperateSelfError)
		}
		adminRoleIds := adminLogic.GetRoleIdsByAdminId(ctx, pb.Admin.Id)
		authRoleIds := adminLogic.GetRoleIdsByAdminId(ctx, pb.OperaterId)
		if len(utils.ArrayIntersect(adminRoleIds, authRoleIds)) > 0 {
			return nil, businessError.ErrorInfoToRpcError(businessError.AdminOperateSameRoleError)
		}
		adminIds = adminLogic.GetChildrenRoleAdminIds(ctx, pb.OperaterId, false)
		if !utils.InArray(pb.Admin.Id, adminIds) {
			return nil, businessError.ErrorInfoToRpcError(businessError.AdminOperateOtherRoleError)
		}
		if len(utils.ArrayDiff(roleIds, adminLogic.GetChildrenRoleIds(ctx, pb.OperaterId))) > 0 {
			return nil, businessError.ErrorInfoToRpcError(businessError.AdminEditOnlySelectYourRole)
		}
	}

	Db := database.Db.GetDefaultDb(ctx)

	row, dbErr := Db.Find("select * from admin where id=? and id in (?)", pb.Admin.Id, adminIds)

	if dbErr != nil {
		return nil, businessError.DbError(dbErr)
	}

	if row == nil {
		return nil, businessError.ErrorInfoToRpcError(businessError.NotFound)
	}

	if pb.Admin.Email != fmt.Sprintf("%v", row["email"]) {
		hasEmail, dbErr := Db.Find("select * from admin where email=?", pb.Admin.Email)

		if dbErr != nil {
			return nil, businessError.DbError(dbErr)
		}

		if hasEmail != nil {
			return nil, businessError.ErrorInfoToRpcError(businessError.AdminEmailExists)
		}
	}

	if pb.Admin.Username != fmt.Sprintf("%v", row["username"]) {
		hasUsername, dbErr := Db.Find("select * from admin where username=?", pb.Admin.Username)

		if dbErr != nil {
			return nil, businessError.DbError(dbErr)
		}

		if hasUsername != nil {
			return nil, businessError.ErrorInfoToRpcError(businessError.AdminUsernameExists)
		}
	}

	if pb.Admin.Password != "" {
		pb.Admin.Salt = utils.Md5(strconv.FormatInt(int64(rand.Intn(99999-10000)+10000), 10) + utils.Date("Y-m-d H:i:s"))[:6]
		pb.Admin.Password = utils.Md5(utils.Md5(pb.Admin.Password) + pb.Admin.Salt)
		pb.Admin.Token = ""
	} else {
		pb.Admin.Salt = row["salt"].(string)
		pb.Admin.Password = row["password"].(string)
	}

	_, e := Db.TransactionFunc(func(Db *db.Db) (interface{}, error) {

		rowCount, dbErr := Db.Update("admin", map[string]interface{}{
			"username":    pb.Admin.Username,
			"email":       pb.Admin.Email,
			"token":       pb.Admin.Token,
			"password":    pb.Admin.Password,
			"salt":        pb.Admin.Salt,
			"nickname":    pb.Admin.Nickname,
			"avatar":      pb.Admin.Avatar,
			"status":      pb.Admin.Status,
			"update_time": utils.Date("Y-m-d H:i:s"),
		}, map[string]interface{}{
			"id": pb.Admin.Id,
		})

		if dbErr != nil {
			return nil, businessError.ErrorInfoToRpcError(businessError.DbError(dbErr))
		}

		if rowCount <= 0 {
			return nil, businessError.ErrorInfoToRpcError(businessError.OperateError)
		}

		hasRole, dbErr := Db.Query("select role_id from admin_role_access where admin_id=?", row["id"])
		if dbErr != nil {
			return nil, businessError.ErrorInfoToRpcError(businessError.DbError(dbErr))
		}

		hasRoleIds := utils.ArrayColumn(hasRole, "role_id")
		addRoleIds := utils.ArrayDiff(roleIds, hasRoleIds)
		delRoleIds := utils.ArrayDiff(hasRoleIds, roleIds)
		if len(delRoleIds) > 0 {
			rowCount, dbErr := Db.Delete("admin_role_access", map[string]interface{}{
				"admin_id": row["id"],
				"role_id":  delRoleIds,
			})
			if dbErr != nil {
				return nil, businessError.ErrorInfoToRpcError(businessError.DbError(dbErr))
			}

			if rowCount <= 0 {
				return nil, businessError.ErrorInfoToRpcError(businessError.OperateError)
			}
		}

		for _, roleId := range addRoleIds {
			rowCount, dbErr := Db.Insert("admin_role_access", map[string]interface{}{
				"admin_id":    row["id"],
				"role_id":     roleId,
				"update_time": utils.Date("Y-m-d H:i:s"),
				"create_time": utils.Date("Y-m-d H:i:s"),
			})

			if dbErr != nil {
				return nil, businessError.ErrorInfoToRpcError(businessError.DbError(dbErr))
			}

			if rowCount <= 0 {
				return nil, businessError.ErrorInfoToRpcError(businessError.AdminAddError)
			}
		}

		return nil, nil
	})

	if e != nil {
		return nil, e
	}

	return &pbAdmin.AdminEditResp{}, nil
}
